﻿using System;
using System.Linq;
using ThoughtWorks.CruiseControl.Core;
using Exortech.NetReflector;
using ThoughtWorks.CruiseControl.Remote;
using agsXMPP;
using agsXMPP.protocol.client;
using System.Threading;
using Message = agsXMPP.protocol.client.Message;


namespace CCNet.Jabber.Plugin
{
    /// <summary>
    /// 
    /// 
    /// 
    /// 
    /// </summary>
    [ReflectorType("jabber")]
    public class JabberPublisherTask : ThoughtWorks.CruiseControl.Core.ITask
    {
        private int _port = 5222;

        private static bool _bWait;

        private bool _autoResolveConnectServer = true;

        private ThoughtWorks.CruiseControl.Core.IIntegrationResult _integrationResult;

        /// <summary>
        /// Gets or sets the host.
        /// </summary>
        /// <value>
        /// The host.
        /// </value>
        [ReflectorProperty("host", Required = false)]
        public string Host { get; set; }

        /// <summary>
        /// Gets or sets the port.
        /// </summary>
        /// <value>
        /// The port.
        /// </value>
        [ReflectorProperty("port", Required = false)]
        public int Port
        {
            get { return _port; }
            set { _port = value; }
        }

        /// <summary>
        /// Gets or sets the username.
        /// </summary>
        /// <value>
        /// The username.
        /// </value>
        [ReflectorProperty("username", Required = true)]
        public string Username { get; set; }

        /// <summary>
        /// Gets or sets the password.
        /// </summary>
        /// <value>
        /// The password.
        /// </value>
        [ReflectorProperty("password", Required = true)]
        public string Password { get; set; }

        /// <summary>
        /// Gets or sets the recipients.
        /// </summary>
        /// <value>
        /// The recipients.
        /// </value>
        [ReflectorArray(@"recipients", Required = true)]
        public Recipient[] Recipients { get; set; }

        /// <summary>
        /// Gets or sets the message.
        /// </summary>
        /// <value>
        /// The message.
        /// </value>
        [ReflectorProperty(@"message", Required = false)]
        public string Message { get; set; }

        /// <summary>
        /// Gets or sets a value indicating whether [auto resolve connect server].
        /// </summary>
        /// <value>
        /// 	<c>true</c> if [auto resolve connect server]; otherwise, <c>false</c>.
        /// </value>
        [ReflectorProperty(@"autoResolveConnectServer", Required = false)]
        public bool AutoResolveConnectServer
        {
            get { return _autoResolveConnectServer; } 
            set { _autoResolveConnectServer = value; }
        }


        /// <summary>
        /// Runs the task, given the specified <see cref="T:ThoughtWorks.CruiseControl.Core.IIntegrationResult"/>, in the specified <see cref="T:ThoughtWorks.CruiseControl.Core.IProject"/>.
        /// </summary>
        /// <param name="result"></param>
        public void Run(IIntegrationResult result)
        {
      
            if (Eval(result))
            {
                //Set the variable for all the call back handlers.
                _integrationResult = result;

                //Jab the receipients. 
                SendMessage();
            }

        }

        /// <summary>
        /// Evals the specified result.
        /// </summary>
        /// <param name="result">The result.</param>
        /// <returns></returns>
        private bool Eval(IIntegrationResult result)
        {
            if (result == null)
                return false;
            else
                return true;
        }


        // Ok, the meat starts here !! :) 
        public void SendMessage()
        {

            XmppClientConnection client = new XmppClientConnection();


            Jid jUser = new Jid(Username);

            client.AutoResolveConnectServer = AutoResolveConnectServer;
            client.AutoAgents = false;
            client.Username = jUser.User;
            client.Server = jUser.Server;
            client.Password = Password;
            client.AutoPresence = false;
            client.AutoRoster = false;
            client.AutoAgents = false;

            try
            {
                client.OnAuthError += new XmppElementHandler(client_OnAuthError);
                client.OnLogin += new ObjectHandler(client_OnLogin);
                client.OnError += new ErrorHandler(client_OnError);

                // Open the connection.
                client.Open();
            }
            catch
            {
                throw;
            }

            //Since client.open() is an asynchronous method. We need to wait and want it to call us back when its done.
            Wait();
        }


        /// <summary>
        /// Waits this instance.
        /// </summary>
        private static void Wait()
        {
            int i = 0;
            _bWait = true;

            while (_bWait)
            {
                i++;
                if (i == 60)
                    _bWait = false;

                Thread.Sleep(500);
            }
        }

        /// <summary>
        /// Client_s the on auth error.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The e.</param>
        private void client_OnAuthError(object sender, agsXMPP.Xml.Dom.Element e)
        {
            throw new Exception(string.Format("Authentication for user '{0}' failed.", Username));
        }


        /// <summary>
        /// Client_s the on error.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="ex">The ex.</param>
        private void client_OnError(object sender, Exception ex)
        {
            throw ex;
        }


        //
        /// <summary>
        /// Message the recipients when the login is successfull.
        /// </summary>
        /// <param name="sender">The sender.</param>
        private void client_OnLogin(object sender)
        {
            _bWait = false;

            XmppClientConnection client = (XmppClientConnection) sender;

            foreach (Recipient r in Recipients)
                try
                {
                    client.Send(new Message(r.JabberID, MessageType.chat, GetMessage()));
                }catch
                {
                }


            client.Close();

        }

      
        /// <summary>
        /// Returns a formatted message for jabbing.
        /// </summary>
        /// <returns></returns>
        private string GetMessage()
        {
            if (string.IsNullOrEmpty(Message))
                return string.Format("[ccnet notification]:The Build is '{0}' for '{1}'. " +
                                     "Build forced by {2}", _integrationResult.Status.ToString().ToUpper(),
                                     _integrationResult.ProjectName, _integrationResult.IntegrationRequest.UserName);
            else
                return Message;
        }

    }
}
